home *** CD-ROM | disk | FTP | other *** search
- /*
- * File: rstruct.r
- * Contents: addmem, cplist, cpset, hmake, hchain, hfirst, hnext, hgrow,
- * hshrink, memb
- */
-
- /*
- * addmem - add a new set element block in the correct spot in
- * the bucket chain.
- */
-
- novalue addmem(ps,pe,pl)
- union block **pl;
- struct b_set *ps;
- struct b_selem *pe;
- {
- ps->size++;
- if (*pl != NULL )
- pe->clink = *pl;
- *pl = (union block *) pe;
- }
-
- /*
- * cplist(dp1,dp2,i,j) - copy sublist dp1[i:j] into dp2.
- */
-
- int cplist(dp1, dp2, i, j)
- dptr dp1, dp2;
- word i, j;
- {
- register dptr dp;
- word size, nslots;
- tended struct b_list *lp1, *lp2;
- tended struct b_lelem *bp1, *bp2;
-
- /*
- * Calculate the size of the sublist.
- */
- size = nslots = j - i;
- if (nslots == 0)
- nslots = MinListSlots;
-
- /*
- * Get pointers to the list and list elements for the source list
- * (bp1, lp1) and the sublist (bp2, lp2).
- */
- lp1 = (struct b_list *) BlkLoc(*dp1);
- bp1 = (struct b_lelem *) lp1->listhead;
- Protect(lp2 = (struct b_list *) alclist(size), return Error);
- Protect(bp2 = (struct b_lelem *)alclstb(nslots,(word)0,size), return Error);
- lp2->listhead = lp2->listtail = (union block *) bp2;
- dp = bp2->lslots;
-
- /*
- * Locate the block containing element i in the source list.
- */
- if (size > 0) {
- while (i > bp1->nused) {
- i -= bp1->nused;
- bp1 = (struct b_lelem *) bp1->listnext;
- }
- }
-
- /*
- * Copy elements from the source list into the sublist, moving to
- * the next list block in the source list when all elements in a
- * block have been copied.
- */
- while (size > 0) {
- j = bp1->first + i - 1;
- if (j >= bp1->nslots)
- j -= bp1->nslots;
- *dp++ = bp1->lslots[j];
- if (++i > bp1->nused) {
- i = 1;
- bp1 = (struct b_lelem *) bp1->listnext;
- }
- size--;
- }
-
- /*
- * Fix type and location fields for the new list.
- */
- dp2->dword = D_List;
- BlkLoc(*dp2) = (union block *) lp2;
- return Succeeded;
- }
-
- /*
- * cpset(dp1,dp2,n) - copy set dp1 to dp2, reserving memory for n entries.
- */
- int cpset(dp1, dp2, n)
- dptr dp1, dp2;
- word n;
- {
- union block *src;
- tended union block *dst;
- tended struct b_slots *seg;
- tended struct b_selem *ep, *prev;
- struct b_selem *se;
- register word slotnum;
- register int i;
-
- /*
- * Make a new set organized like dp1, with room for n elements.
- */
- dst = hmake(T_Set, BlkLoc(*dp1)->set.mask + 1, n);
- if (dst == NULL)
- return Error;
- /*
- * Copy the header and slot blocks.
- */
- src = BlkLoc(*dp1);
- dst->set.size = src->set.size; /* actual set size */
- dst->set.mask = src->set.mask; /* hash mask */
- for (i = 0; i < HSegs && src->set.hdir[i] != NULL; i++)
- memcopy((char *)dst->set.hdir[i], (char *)src->set.hdir[i],
- src->set.hdir[i]->blksize);
- /*
- * Work down the chain of element blocks in each bucket
- * and create identical chains in new set.
- */
- for (i = 0; i < HSegs && (seg = dst->set.hdir[i]) != NULL; i++)
- for (slotnum = segsize[i] - 1; slotnum >= 0; slotnum--) {
- prev = NULL;
- for (ep = (struct b_selem *)seg->hslots[slotnum];
- ep != NULL; ep = (struct b_selem *)ep->clink) {
- Protect(se = alcselem(&ep->setmem, ep->hashnum), return Error);
- if (prev == NULL)
- seg->hslots[slotnum] = (union block *)se;
- else
- prev->clink = (union block *)se;
- se->clink = ep->clink;
- prev = se;
- }
- }
- dp2->dword = D_Set;
- BlkLoc(*dp2) = dst;
- if (TooSparse(dst))
- hshrink(dst);
- return Succeeded;
- }
-
- /*
- * hmake - make a hash structure (Set or Table) with a given number of slots.
- * If *nslots* is zero, a value appropriate for *nelem* elements is chosen.
- * A return of NULL indicates allocation failure.
- */
- union block *hmake(tcode, nslots, nelem)
- int tcode;
- word nslots, nelem;
- {
- word seg, t, blksize, elemsize;
- tended union block *blk;
- struct b_slots *segp;
-
- if (nslots == 0)
- nslots = (nelem + MaxHLoad - 1) / MaxHLoad;
- for (seg = t = 0; seg < (HSegs - 1) && (t += segsize[seg]) < nslots; seg++)
- ;
- nslots = ((word)HSlots) << seg; /* ensure legal power of 2 */
- if (tcode == T_Table) {
- blksize = sizeof(struct b_table);
- elemsize = sizeof(struct b_telem);
- }
- else { /* T_Set */
- blksize = sizeof(struct b_set);
- elemsize = sizeof(struct b_selem);
- }
- if (!blkreserve((word)(blksize + (seg + 1) * sizeof(struct b_slots)
- + (nslots - HSlots * (seg + 1)) * sizeof(union block *)
- + nelem * elemsize))) return NULL;
- Protect(blk = alchash(tcode), return NULL);
- for (; seg >= 0; seg--) {
- Protect(segp = alcsegment(segsize[seg]), return NULL);
- blk->set.hdir[seg] = segp;
- }
- blk->set.mask = nslots - 1;
- return blk;
- }
-
- /*
- * hchain - return a pointer to the word that points to the head of the hash
- * chain for hash number hn in hashed structure s.
- */
-
- /*
- * lookup table for log to base 2; must have powers of 2 through (HSegs-1)/2.
- */
- static unsigned char log2h[] = {
- 0,1,2,2, 3,3,3,3, 4,4,4,4, 4,4,4,4, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5,
- };
-
- union block **hchain(pb, hn)
- union block *pb;
- register uword hn;
- {
- register struct b_set *ps;
- register word slotnum, segnum, segslot;
-
- ps = (struct b_set *)pb;
- slotnum = hn & ps->mask;
- if (slotnum >= HSlots * sizeof(log2h))
- segnum = log2h[slotnum >> (LogHSlots + HSegs/2)] + HSegs/2;
- else
- segnum = log2h[slotnum >> LogHSlots];
- segslot = hn & (segsize[segnum] - 1);
- return &ps->hdir[segnum]->hslots[segslot];
- }
-
- /*
- * hgfirst - initialize for generating set or table, and return first element.
- */
-
- union block *hgfirst(bp, s)
- union block *bp;
- struct hgstate *s;
- {
- int i;
-
- s->segnum = 0; /* set initial state */
- s->slotnum = -1;
- s->tmask = bp->table.mask;
- for (i = 0; i < HSegs; i++)
- s->sghash[i] = s->sgmask[i] = 0;
- return hgnext(bp, s, (union block *)0); /* get and return first value */
- }
-
- /*
- * hgnext - return the next element of a set or table generation sequence.
- *
- * We carefully generate each element exactly once, even if the hash chains
- * are split between calls. We do this by recording the state of things at
- * the time of the split and checking past history when starting to process
- * a new chain.
- *
- * Elements inserted or deleted between calls may or may not be generated.
- *
- * We assume that no structure *shrinks* after its initial creation; they
- * can only *grow*.
- */
-
- union block *hgnext(bp, s, ep)
- union block *bp;
- struct hgstate *s;
- union block *ep;
- {
- int i;
- word d, m;
- uword hn;
-
- /*
- * Check to see if the set or table's hash buckets were split (once or
- * more) since the last call. We notice this unless the next entry
- * has same hash value as the current one, in which case we defer it
- * by doing nothing now.
- */
- if (bp->table.mask != s->tmask &&
- (ep->telem.clink == NULL ||
- ep->telem.clink->telem.hashnum != ep->telem.hashnum)) {
- /*
- * Yes, they did split. Make a note of the current state.
- */
- hn = ep->telem.hashnum;
- for (i = 1; i < HSegs; i++)
- if ((((word)HSlots) << (i - 1)) > s->tmask) {
- /*
- * For the newly created segments only, save the mask and
- * hash number being processed at time of creation.
- */
- s->sgmask[i] = s->tmask;
- s->sghash[i] = hn;
- }
- s->tmask = bp->table.mask;
- /*
- * Find the next element in our original segment by starting
- * from the beginning and skipping through the current hash
- * number. We can't just follow the link from the current
- * element, because it may have moved to a new segment.
- */
- ep = bp->table.hdir[s->segnum]->hslots[s->slotnum];
- while (ep != NULL && ep->telem.hashnum <= hn)
- ep = ep->telem.clink;
- }
-
- else {
- /*
- * There was no split, or else if there was we're between items
- * that have identical hash numbers. Find the next element in
- * the current hash chain.
- */
- if (ep != NULL) /* already NULL on very first call */
- ep = ep->telem.clink; /* next element in chain, if any */
- }
-
- /*
- * If we don't yet have an element, search successive slots.
- */
- while (ep == NULL) {
- /*
- * Move to the next slot and pick the first entry.
- */
- s->slotnum++;
- if (s->slotnum >= segsize[s->segnum]) {
- s->slotnum = 0; /* need to move to next segment */
- s->segnum++;
- if (s->segnum >= HSegs || bp->table.hdir[s->segnum] == NULL)
- return 0; /* return NULL at end of set/table */
- }
- ep = bp->table.hdir[s->segnum]->hslots[s->slotnum];
- /*
- * Check to see if parts of this hash chain were already processed.
- * This could happen if the elements were in a different chain,
- * but a split occurred while we were suspended.
- */
- for (i = s->segnum; (m = s->sgmask[i]) != 0; i--) {
- d = (word)(m & s->slotnum) - (word)(m & s->sghash[i]);
- if (d < 0) /* if all elements processed earlier */
- ep = NULL; /* skip this slot */
- else if (d == 0) {
- /*
- * This chain was split from its parent while the parent was
- * being processed. Skip past elements already processed.
- */
- while (ep != NULL && ep->telem.hashnum <= s->sghash[i])
- ep = ep->telem.clink;
- }
- }
- }
-
- /*
- * Return the element.
- */
- return ep;
- }
-
- /*
- * hgrow - split a hashed structure (doubling the buckets) for faster access.
- */
-
- novalue hgrow(bp)
- union block *bp;
- {
- register union block **tp0, **tp1, *ep;
- register word newslots, slotnum, segnum;
- tended struct b_set *ps;
- struct b_slots *seg, *newseg;
- union block **curslot;
-
- ps = (struct b_set *) bp;
- if (ps->hdir[HSegs-1] != NULL)
- return; /* can't split further */
- newslots = ps->mask + 1;
- Protect(newseg = alcsegment(newslots), return);
-
- curslot = newseg->hslots;
- for (segnum = 0; (seg = ps->hdir[segnum]) != NULL; segnum++)
- for (slotnum = 0; slotnum < segsize[segnum]; slotnum++) {
- tp0 = &seg->hslots[slotnum]; /* ptr to tail of old slot */
- tp1 = curslot++; /* ptr to tail of new slot */
- for (ep = *tp0; ep != NULL; ep = ep->selem.clink) {
- if ((ep->selem.hashnum & newslots) == 0) {
- *tp0 = ep; /* element does not move */
- tp0 = &ep->selem.clink;
- }
- else {
- *tp1 = ep; /* element moves to new slot */
- tp1 = &ep->selem.clink;
- }
- }
- *tp0 = *tp1 = NULL;
- }
- ps->hdir[segnum] = newseg;
- ps->mask = (ps->mask << 1) | 1;
- }
-
- /*
- * hshrink - combine buckets in a set or table that is too sparse.
- *
- * Call this only for newly created structures. Shrinking an active structure
- * can wreak havoc on suspended generators.
- */
- novalue hshrink(bp)
- union block *bp;
- {
- register union block **tp, *ep0, *ep1;
- int topseg, curseg;
- word slotnum;
- tended struct b_set *ps;
- struct b_slots *seg;
- union block **uppslot;
-
- ps = (struct b_set *)bp;
- topseg = 0;
- for (topseg = 1; topseg < HSegs && ps->hdir[topseg] != NULL; topseg++)
- ;
- topseg--;
- while (TooSparse(ps)) {
- uppslot = ps->hdir[topseg]->hslots;
- ps->hdir[topseg--] = NULL;
- for (curseg = 0; (seg = ps->hdir[curseg]) != NULL; curseg++)
- for (slotnum = 0; slotnum < segsize[curseg]; slotnum++) {
- tp = &seg->hslots[slotnum]; /* tail pointer */
- ep0 = seg->hslots[slotnum]; /* lower slot entry pointer */
- ep1 = *uppslot++; /* upper slot entry pointer */
- while (ep0 != NULL && ep1 != NULL)
- if (ep0->selem.hashnum < ep1->selem.hashnum) {
- *tp = ep0;
- tp = &ep0->selem.clink;
- ep0 = ep0->selem.clink;
- }
- else {
- *tp = ep1;
- tp = &ep1->selem.clink;
- ep1 = ep1->selem.clink;
- }
- while (ep0 != NULL) {
- *tp = ep0;
- tp = &ep0->selem.clink;
- ep0 = ep0->selem.clink;
- }
- while (ep1 != NULL) {
- *tp = ep1;
- tp = &ep1->selem.clink;
- ep1 = ep1->selem.clink;
- }
- }
- ps->mask >>= 1;
- }
- }
-
- /*
- * memb - sets res flag to 1 if x is a member of a set or table, or to 0 if not.
- * Returns a pointer to the word which points to the element, or which
- * would point to it if it were there.
- */
-
- union block **memb(pb, x, hn, res)
- union block *pb;
- dptr x;
- register uword hn;
- int *res; /* pointer to integer result flag */
- {
- struct b_set *ps;
- register union block **lp;
- register struct b_selem *pe;
- register uword eh;
-
- ps = (struct b_set *)pb;
- lp = hchain(pb, hn);
- /*
- * Look for x in the hash chain.
- */
- *res = 0;
- while ((pe = (struct b_selem *)*lp) != NULL) {
- eh = pe->hashnum;
- if (eh > hn) /* too far - it isn't there */
- return lp;
- else if ((eh == hn) && (equiv(&pe->setmem, x))) {
- *res = 1;
- return lp;
- }
- /*
- * We haven't reached the right hashnumber yet or
- * the element isn't the right one so keep looking.
- */
- lp = &(pe->clink);
- }
- /*
- * At end of chain - not there.
- */
- return lp;
- }
-